package com.h5ve.flow.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import com.h5ve.base.api.R;
import com.h5ve.log.annotation.SysLog;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.dromara.warm.flow.core.entity.Definition;
import org.dromara.warm.flow.core.service.DefService;
import org.dromara.warm.flow.core.utils.page.Page;
import org.dromara.warm.flow.orm.entity.FlowDefinition;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

/**
 * 流程定义Controller
 *
 * @author hh
 * @date 2023-04-11
 */
@Slf4j
@Validated
@RestController
@RequestMapping("/flow/definition")
public class DefController {
    @Resource
    private DefService defService;

    /**
     * 分页查询流程定义列表
     */
    @GetMapping("/list")
    public R list(Page page, FlowDefinition flowDefinition) {
        // flow组件自带分页功能
        page = defService.orderByCreateTime().desc().page(flowDefinition, page);
        return R.ok(page.getList(), page.getTotal());
    }


    /**
     * 获取流程定义详细信息
     */
    @SaCheckPermission("definition_view")
    @GetMapping(value = "/{id}")
    public R<Definition> getInfo(@PathVariable("id") Long id) {
        return R.ok(defService.getById(id));
    }

    /**
     * 新增流程定义
     */
    @SaCheckPermission("definition_add")
    @SysLog("新增流程定义")
    @PostMapping
    @Transactional(rollbackFor = Exception.class)
    public R<Boolean> add(@RequestBody FlowDefinition flowDefinition) {
        return R.ok(defService.saveAndInitNode(flowDefinition));
    }

    /**
     * 发布流程定义
     */
    @SaCheckPermission("definition_publish")
    @SysLog("发布流程定义")
    @GetMapping("/publish/{id}")
    @Transactional(rollbackFor = Exception.class)
    public R<Boolean> publish(@PathVariable("id") Long id) {
        return R.ok(defService.publish(id));
    }

    /**
     * 取消发布流程定义
     */
    @SaCheckPermission("definition_publish")
    @SysLog("取消发布流程定义")
    @GetMapping("/unPublish/{id}")
    @Transactional(rollbackFor = Exception.class)
    public R<Void> unPublish(@PathVariable("id") Long id) {
        defService.unPublish(id);
        return R.ok();
    }

    /**
     * 修改流程定义
     */
    @SaCheckPermission("definition_edit")
    @SysLog("修改流程定义")
    @PutMapping
    @Transactional(rollbackFor = Exception.class)
    public R<Boolean> edit(@RequestBody FlowDefinition flowDefinition) {
        return R.ok(defService.updateById(flowDefinition));
    }

    /**
     * 删除流程定义
     */
    @SaCheckPermission("definition_del")
    @SysLog("删除流程定义")
    @DeleteMapping("/{ids}")
    @Transactional(rollbackFor = Exception.class)
    public R<Boolean> remove(@PathVariable List<Long> ids) {
        return R.ok(defService.removeDef(ids));
    }

    /**
     * 复制流程定义
     */
    @SaCheckPermission("definition_publish")
    @SysLog("复制流程定义")
    @GetMapping("/copyDef/{id}")
    @Transactional(rollbackFor = Exception.class)
    public R<Boolean> copyDef(@PathVariable("id") Long id) {
        return R.ok(defService.copyDef(id));
    }

    @SysLog("导入流程定义")
    @SaCheckPermission("definition_import")
    @PostMapping("/importDefinition")
    @Transactional(rollbackFor = Exception.class)
    public R<Void> importDefinition(MultipartFile file) throws Exception {
        defService.importXml(file.getInputStream());
        return R.ok();
    }

    @SysLog("导出流程定义")
    @SaCheckPermission("definition_export")
    @PostMapping("/exportDefinition/{id}")
    public void exportDefinition(@PathVariable("id") Long id, HttpServletResponse response) throws Exception {
        Document document = defService.exportXml(id);
        // 设置生成xml的格式
        OutputFormat of = OutputFormat.createPrettyPrint();
        // 设置编码格式
        of.setEncoding("UTF-8");
        of.setIndent(true);
        of.setIndent("    ");
        of.setNewlines(true);

        // 创建一个xml文档编辑器
        XMLWriter writer = new XMLWriter(response.getOutputStream(), of);
        writer.setEscapeText(false);
        response.reset();
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/x-msdownload");
        response.setHeader("Content-Disposition", "attachment;");
        writer.write(document);
        writer.close();
    }

    /**
     * 查询流程图
     *
     * @param definitionId
     * @return
     * @throws IOException
     */
    @GetMapping("/flowChartNoColor/{definitionId}")
    public R<String> flowChartNoColor(@PathVariable("definitionId") Long definitionId) throws IOException {
        return R.ok(defService.flowChartNoColor(definitionId));
    }

    /**
     * 查询流程图
     *
     * @param instanceId
     * @return
     * @throws IOException
     */
    @GetMapping("/flowChart/{instanceId}")
    public R<String> flowChart(@PathVariable("instanceId") Long instanceId) throws IOException {
        return R.ok(defService.flowChart(instanceId));
    }

    /**
     * 挂起流程
     *
     * @param definitionId
     * @return
     */
    @GetMapping("/unActive/{definitionId}")
    public R<Boolean> unActive(@PathVariable("definitionId") Long definitionId) {
        return R.ok(defService.unActive(definitionId));
    }

    @SysLog("新增流程定义xml")
    @SaCheckPermission("definition_add")
    @PostMapping("/saveXml")
    @Transactional(rollbackFor = Exception.class)
    public R<Void> saveXml(@RequestBody FlowDefinition def) throws Exception {
        defService.saveXml(def);
        return R.ok();
    }

    @SysLog("获取流程定义xml字符串")
    @SaCheckPermission("definition_export")
    @GetMapping("/xmlString/{id}")
    public R<String> xmlString(@PathVariable("id") Long id) {
        try {
            String xmlStr = defService.xmlString(id);
            return R.ok(xmlStr);
        } catch (Exception ex) {
            log.error(ex.getMessage());
        }
        return R.ok();
    }
}
